﻿using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing.Navigation;
using Microsoft.SharePoint.Taxonomy;

namespace FriendlyURLsForListItemsSP2013
{
    public class BlogPostAddedOrUpdatedEventReceiver : SPItemEventReceiver
    {
        private bool eventFiringEnabledStatus;

        public override void ItemAdded(SPItemEventProperties properties)
        {
            base.ItemAdded(properties);
            UpdateFriendlyUrl(properties);
        }

        /// <summary>
        /// Note: this method will only work if the List is located in the same site collection where
        ///         the Managed Navigation is configured to point to the Taxonomy Navigation term set
        /// </summary>
        /// <param name="properties"></param>
        private void UpdateFriendlyUrl(SPItemEventProperties properties)
        {
            try
            {
                if (properties.ListItem == null || properties.Web == null) return;

                EventFiringEnabled = false;

                var addedItem = properties.ListItem;
                var webUrl = properties.Web.Url;

                // first, read relevant existing metadata from current item
                var enteredTitle = addedItem["Title"] as string;
                var existingFriendlyUrl = "";
                if (addedItem["ListItemMappedFriendlyURL"] != null &&
                    !String.IsNullOrEmpty(addedItem["ListItemMappedFriendlyURL"].ToString()))
                {
                    var existingFriendlyUrlFldVl = new SPFieldUrlValue(addedItem["ListItemMappedFriendlyURL"].ToString());
                    if (!String.IsNullOrEmpty(existingFriendlyUrlFldVl.Url))
                        existingFriendlyUrl = existingFriendlyUrlFldVl.Url;
                }

                
                var newFriendlyUrl = String.Empty;
                var newFriendlyUrlTermId = String.Empty;
                var createTermResult = false;
                SPSecurity.RunWithElevatedPrivileges(delegate
                {
                    using (var site = new SPSite(webUrl))
                    {
                        using (var web = site.OpenWeb())
                        {
                            // TODO: when implementing this solution, replace this URL with the appropriate value for
                            //          the friendly URL of the navigation term that will be the parent of your newly created terms
                            var parentFriendlyTermsUrl = web.Url + "/blog";

                            //now create a taxonomy term in the navigation term set with the title in the url, and return relevant values
                            createTermResult = CreateTermForBlog(enteredTitle, web, existingFriendlyUrl, parentFriendlyTermsUrl, out newFriendlyUrl, out newFriendlyUrlTermId);
                        }
                    }
                });

                // if we failed to generate a valid friendly URL, don't proceed.
                if (String.IsNullOrEmpty(newFriendlyUrl) || !createTermResult) return;

                // set the friendly url & navigation taxonomy term id field
                var newFriendlyUrlFieldVal = new SPFieldUrlValue { Description = newFriendlyUrl, Url = newFriendlyUrl };
                addedItem["ListItemMappedFriendlyURL"] = newFriendlyUrlFieldVal;

                // set the friendly url taxonomy term ID if we have it.
                if (!String.IsNullOrEmpty(newFriendlyUrlTermId)) 
                    addedItem["ListItemNavigationTermId"] = "#0" + newFriendlyUrlTermId;
                // the #0 is added because the {Term.id} property in the web part query parameters list has that at the beginning for some reason
                //   http://technet.microsoft.com/en-us/library/jj683123.aspx

                addedItem.Update();
            }
            catch (Exception outerEx)
            {
                // TODO: log exception.
                throw;
            }
            finally
            {
                EventFiringEnabled = true;
            }
        }

        /// <summary>
        /// Goes into the taxonomy and creates & associates a navigation term with the specified list item information
        /// </summary>
        /// <param name="itemTitle"></param>
        /// <param name="web"></param>
        /// <param name="existingFriendlyUrl"></param>
        /// <param name="parentTermFriendlyUrl">this URL must be server-relative</param>
        /// <param name="newFriendlyUrl"></param>
        /// <param name="newFriendlyUrlTermId"></param>
        /// <returns></returns>
        private bool CreateTermForBlog(String itemTitle, SPWeb web, String existingFriendlyUrl, String parentTermFriendlyUrl, out String newFriendlyUrl, out String newFriendlyUrlTermId)
        {
            try
            {
                newFriendlyUrl = String.Empty;
                newFriendlyUrlTermId = String.Empty;

                if (web == null) return false;

                // grab the managed navigation term set configured for the current web 
                //      (if it's not properly configured, this will fail)
                var globalNavTaxTermSet = TaxonomyNavigation.GetTermSetForWeb(web, StandardNavigationProviderNames.GlobalNavigationTaxonomyProvider, true);

                // NOTE: this method is NOT efficient, but when I use globalnavTaxTermSet with the FindTermForUrl() method, 
                //          it always fails due to the fact that somehow the term set has no child nodes in it...
                //      so for now we do this roundabout method of creating a new context and re-retrieving the "editable" term set.
                var taxSession = new TaxonomySession(web.Site, true);
                var editableNavTs = globalNavTaxTermSet.GetAsEditable(taxSession);
                var taxTsFromNavTs = editableNavTs.GetTaxonomyTermSet();
                var resolvedNavTs = NavigationTermSet.GetAsResolvedByWeb(taxTsFromNavTs, web, StandardNavigationProviderNames.GlobalNavigationTaxonomyProvider);

                // retrieve the parent navigation term
                var parentTerm = resolvedNavTs.FindTermForUrl(parentTermFriendlyUrl);
                if (parentTerm == null)
                    return false;

                // either create or modify the navigation term underneath the parent
                NavigationTerm newBlogTerm = null;
                if (!String.IsNullOrEmpty(existingFriendlyUrl))
                {
                    // term has already been created, find it!
                    newBlogTerm = globalNavTaxTermSet.FindTermForUrl(existingFriendlyUrl);
                }

                if (newBlogTerm == null)
                {
                    // term doesn't exist, create a new one  (physical url inherited from /blog parent term)
                    newBlogTerm = parentTerm.CreateTerm(itemTitle, NavigationLinkType.FriendlyUrl);
                    parentTerm.GetTaxonomyTerm().TermStore.CommitAll();
                    newFriendlyUrl = newBlogTerm.GetWebRelativeFriendlyUrl();
                    newFriendlyUrlTermId = newBlogTerm.Id.ToString();
                }
                else
                {
                    // term already exists, simply modify the title
                    var newBlogNavEditableTerm = newBlogTerm.GetAsEditable(taxSession);
                    newBlogNavEditableTerm.FriendlyUrlSegment.Value = itemTitle.Replace(' ', '-');
                    var newBlogTaxTerm = newBlogNavEditableTerm.GetTaxonomyTerm();
                    newBlogTaxTerm.Name = itemTitle;
                    newBlogTaxTerm.TermStore.CommitAll();
                }

                return true;
            }
            catch (Exception ex)
            {
                // TODO: log exception

                newFriendlyUrl = "";
                newFriendlyUrlTermId = "";
                return false;
            }
        }

        public override void ItemUpdated(SPItemEventProperties properties)
        {
            base.ItemUpdated(properties);
            UpdateFriendlyUrl(properties);
        }

        private void DisableItemEventsScope()
        {
            eventFiringEnabledStatus = EventFiringEnabled;
            EventFiringEnabled = false;
        }

        private void EnableItemEventsScope()
        {
            eventFiringEnabledStatus = EventFiringEnabled;
            EventFiringEnabled = true;
        }
    }
}
